home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
language
/
pcl_src.zoo
/
methods.lsp
< prev
next >
Wrap
Text File
|
1992-09-09
|
122KB
|
2,953 lines
;;;-*-Mode:LISP; Package: PCL; Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
;;; All rights reserved.
;;;
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted. Any distribution of this
;;; software or derivative works must comply with all applicable United
;;; States export control laws.
;;;
;;; This software is made available AS IS, and Xerox Corporation makes no
;;; warranty about the software, its performance or its conformity to any
;;; specification.
;;;
;;; Any person obtaining a copy of this software is requested to send their
;;; name and post office or electronic mail address to:
;;; CommonLoops Coordinator
;;; Xerox PARC
;;; 3333 Coyote Hill Rd.
;;; Palo Alto, CA 94304
;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
;;;
;;; Suggestions, comments and requests for improvements are also welcome.
;;; *************************************************************************
;;;
(in-package 'pcl)
;;;
;;; METHODS
;;;
;;; Methods themselves are simple inanimate objects. Most properties of
;;; methods are immutable, methods cannot be reinitialized. The following
;;; properties of methods can be changed:
;;; METHOD-GENERIC-FUNCTION
;;; METHOD-FUNCTION ??
;;;
;;;
(defclass method (metaobject) ()
(:predicate-name method-p))
(defclass standard-method (definition-source-mixin documentation-mixin plist-mixin method)
((generic-function
:initform nil
:accessor method-generic-function)
; (qualifiers
; :initform ()
; :initarg :qualifiers
; :reader method-qualifiers)
(specializers
:initform ()
:initarg :specializers
:reader method-specializers)
(lambda-list
:initform ()
:initarg :lambda-list
:reader method-lambda-list)
(function
:initform nil
:reader method-function)
(function-name
:initform nil
:accessor method-function-name)
(optimized-function
:initform nil
:accessor method-optimized-function)
(closure-generator
:initform nil
:reader method-closure-generator)
(cached-functions-alist
:initform nil
:accessor method-cached-functions-alist
:documentation
"Alist of all cached functions of method and the slot-locations/fetchers
they're cached for.")
(needs-next-methods-p
:initform T ;Better safe than sorry
:initarg :needs-next-methods-p
:reader method-needs-next-methods-p
:documentation
"Does method call CALL-NEXT-METHOD or NEXT-METHOD-P?")
(optimized-slot-indices
:initform NIL
:type list
:initarg :optimized-slot-indices
:reader method-optimized-slot-indices
:documentation
"List of slot index forms optimized by optimize-instance-access
for slot-values within the method body. Each index is in the
form '(index-var parameter slot-name).")
(optimized-method-lambda
:initform NIL
:type list
:initarg :optimized-method-lambda
:reader method-optimized-method-lambda
:documentation
"The optimized PCL method-lambda. Only stored for methods for
which store-optimized-method-lambda-p is T.")
(identifier
:initform nil
:initarg :identifier
:reader method-identifier
:documentation
"Symbol identifier for method used for THIS-METHOD and NO-NEXT-METHOD.")
)
(:predicate-name standard-method-p))
(defclass standard-accessor-method (standard-method)
((slot-name :initform nil
:initarg :slot-name
:reader accessor-method-slot-name))
(:predicate-name standard-accessor-method-p))
(defclass standard-reader-method (standard-accessor-method) ()
(:predicate-name standard-reader-method-p))
(defclass standard-writer-method (standard-accessor-method) ()
(:predicate-name standard-writer-method-p))
(defclass standard-boundp-method (standard-accessor-method) ()
(:predicate-name standard-boundp-method-p))
(defvar *the-class-method* (find-class 'method))
(defvar *the-class-standard-method* (find-class 'standard-method))
(defvar *the-class-standard-reader-method* (find-class 'standard-reader-method))
(defvar *the-class-standard-writer-method* (find-class 'standard-writer-method))
(defvar *the-class-standard-boundp-method* (find-class 'standard-boundp-method))
(defmethod accessor-method-slot-definition ((method standard-accessor-method))
(let ((slot-name (accessor-method-slot-name method))
(direct-class (car (last (method-specializers method)))))
(dolist (direct-slot (class-direct-slots direct-class))
(when (eq (slot-definition-name direct-slot) slot-name)
(return direct-slot)))))
(defmethod print-object ((method standard-method) stream)
(printing-random-thing (method stream)
(let ((generic-function (method-generic-function method))
(class-name (capitalize-words (class-name (class-of method)))))
(format stream "~A ~S ~{~S ~}~:S"
class-name
(and generic-function (generic-function-name generic-function))
(method-qualifiers method)
(unparse-specializers method)))))
(defmethod print-object ((method standard-accessor-method) stream)
(printing-random-thing (method stream)
(let ((generic-function (method-generic-function method))
(class-name (capitalize-words (class-name (class-of method)))))
(format stream "~A ~S, slot:~S, ~:S"
class-name
(and generic-function (generic-function-name generic-function))
(accessor-method-slot-name method)
(unparse-specializers method)))))
(defmethod method-cached-functions ((method standard-method))
(mapcar #'cdr (slot-value method 'cached-functions-alist)))
(defvar *generic-functions-having-cached-closures* NIL)
(declaim (ftype (function (T T) (values list list))
cached-slot-locations-and-fetchers-from-wrappers))
(defmethod cached-slot-locations-and-fetchers-from-wrappers ((method standard-method)
wrappers)
(declare (type list wrappers))
(if wrappers
(let ((slot-accesses (method-optimized-slot-indices method))
(generic-function (method-generic-function method))
(lambda-list (method-lambda-list method))
(accessed-classes NIL)
(slot-locations NIL)
(slot-fetchers NIL))
(declare (type list lambda-list accessed-classes
slot-locations slot-fetchers))
(dolist (slot-access (reverse slot-accesses))
(let* ((wrapper (nth (the index
(posq (first slot-access) lambda-list))
wrappers))
(class (wrapper-class wrapper))
(slotd (find-slot-definition class (second slot-access))))
(if (and slotd
(or *safe-to-use-slot-wrapper-optimizations-p*
(slot-values-safe-using-class-p class slotd)))
(progn
(push (slot-definition-location slotd) slot-locations)
(unless (memq class accessed-classes)
(let ((cached-in-fns
(fast-slot-value class 'cached-in-generic-functions
slow-slot-value)))
(unless (memq generic-function cached-in-fns)
(fast-set-slot-value class 'cached-in-generic-functions
(cons generic-function cached-in-fns)
slow-slot-value)))
(push class accessed-classes)))
(push NIL slot-locations))
(push (slots-fetcher class) slot-fetchers)))
(values slot-locations slot-fetchers))
(let ((null-list
(make-list
(length (the list (method-optimized-slot-indices method))))))
(values null-list null-list))))
(defmethod get-cached-function ((method standard-method)
slot-locations
&optional slot-fetchers)
(dolist (acons (method-cached-functions-alist method))
(when (and (list-eq (caar acons) slot-locations)
(or (equal (cdar acons) slot-fetchers)
(null slot-fetchers)))
(return (cdr acons)))))
(defmethod add-cached-function ((method standard-method)
function
slot-locations
&optional slot-fetchers)
(let ((new-function
(set-function-name-1
(method-function-storage-form function)
(method-function-name method)
nil))
(cached-functions
(method-cached-functions-alist method)))
#+(and kcl turbo-closure) (si:turbo-closure new-function)
(dolist (acons cached-functions
(setf (method-cached-functions-alist method)
(cons (cons (cons slot-locations slot-fetchers) function)
cached-functions)))
(when (and (every #'eql (caar acons) slot-locations)
(or (equal (cdar acons) slot-fetchers)
(null slot-fetchers)))
(setf (cdr acons) function)
(return)))
(setf (method-function-method new-function) method)
(pushnew (method-generic-function method)
*generic-functions-having-cached-closures* :test #'eq)
new-function))
;;; closure-generators are used only by method-function-for-caching
;;; and make-not-for-caching-method-function (in vector.lisp).
(defmethod method-function-for-caching-p ((method standard-method))
(slot-value method 'closure-generator))
(defmethod method-function-for-caching ((method standard-method) wrappers)
(let* ((closure-generator (slot-value method 'closure-generator)))
(if closure-generator
(multiple-value-bind (slot-locations slot-fetchers)
(cached-slot-locations-and-fetchers-from-wrappers method wrappers)
(or (get-cached-function method slot-locations slot-fetchers)
(add-cached-function
method
(method-function-funcall
closure-generator
(list slot-locations slot-fetchers method))
slot-locations
slot-fetchers)))
(let ((optimized-function (slot-value method 'optimized-function)))
(if optimized-function
optimized-function
(error "~A has neither closure-generator nor optimized-function."
method))))))
(defvar *cons-global-variable-table* (make-hash-table :test #'eq))
(defun get-cons-global-variable (cons)
(declare (type cons cons))
(or (gethash cons *cons-global-variable-table*)
(setf (gethash cons *cons-global-variable-table*)
(let ((cons-var (gentemp ".CONS")))
(eval `(defvar ,cons-var))
(set cons-var cons)
cons-var))))
(defmethod make-cached-method-function-from-stored-lambda
((method standard-method)
slot-locations-and-fetchers)
(compile-lambda (make-cached-method-lambda-from-stored-lambda
method slot-locations-and-fetchers)))
(defmethod make-cached-method-lambda-from-stored-lambda
((method standard-method)
slot-locations-and-fetchers)
(let* ((slot-locations (first slot-locations-and-fetchers))
(slot-fetchers (second slot-locations-and-fetchers))
(method-lambda (slot-value method 'optimized-method-lambda))
(lambda-list (cadr method-lambda))
(body (cddr method-lambda))
(slot-indices
(slot-value method 'optimized-slot-indices))
(used-slot-locations
(mapcar #'(lambda (loc)
(if (consp loc) (get-cons-global-variable loc) loc))
slot-locations)))
(multiple-value-bind (documentation declarations real-body)
(extract-declarations body)
(flet ((access-form (x slot location slots-fetcher)
(cond ((typep location 'fixnum)
`(svref (the simple-vector (,slots-fetcher ,x))
,location))
((and (symbolp location) (not (null location)))
`(cdr ,location))
((null location)
`(fast-slot-value ,x ',slot))
(T (error "Unknown slot-location type ~S" location)))))
`(lambda ,lambda-list
,@declarations
,@documentation
(macrolet
((optimized-parameter-read (x slot index)
(declare (ignore index))
(cond
,@(mapcar
#'(lambda (slot-index loc slots-fetcher)
(let ((slot-param (first slot-index))
(slot-name (second slot-index)))
`((and (eq x ',slot-param)
(eq (second slot) ',slot-name))
,(if loc
``(let ((.value.
,',(access-form slot-param slot-name
loc slots-fetcher)))
(if (eq .value. *slot-unbound*)
(funcall #'slot-value ,',slot-param
',',slot-name)
.value.))
``(fast-slot-value ,',slot-param
',',slot-name)))))
slot-indices used-slot-locations slot-fetchers)))
(optimized-parameter-write (x slot index new)
(declare (ignore index))
(cond
,@(mapcar
#'(lambda (slot-index loc slots-fetcher)
(let ((slot-param (first slot-index))
(slot-name (second slot-index)))
`((and (eq x ',slot-param)
(eq (second slot) ',slot-name))
`(setf ,',(access-form slot-param slot-name
loc slots-fetcher)
,new))))
slot-indices used-slot-locations slot-fetchers)))
(optimized-parameter-boundp (x slot index)
(declare (ignore index))
(cond
,@(mapcar
#'(lambda (slot-index loc slots-fetcher)
(let ((slot-param (first slot-index))
(slot-name (second slot-index)))
`((and (eq x ',slot-param)
(eq (second slot) ',slot-name))
,(if loc
``(neq ,',(access-form slot-param slot-name
loc slots-fetcher)
*slot-unbound*)
``(fast-slot-boundp ,',slot-param
',',slot-name)))))
slot-indices used-slot-locations slot-fetchers))))
,@real-body))))))
(defmethod method-function-for-caching-p ((method standard-accessor-method))
t)
(defmethod method-function-for-caching ((method standard-accessor-method) wrappers)
(let* ((slot-name (slot-value method 'slot-name))
(type (cond ((standard-reader-method-p method) 'reader)
((standard-writer-method-p method) 'writer)
((standard-boundp-method-p method) 'boundp)))
(class (wrapper-class (if (eq type 'writer) (cadr wrappers) (car wrappers))))
(slotd (find-slot-definition class slot-name)))
(if slotd
(let ((slot-accessor-function (slot-accessor-function slotd type)))
(pushnew (cons NIL slot-accessor-function)
(method-cached-functions-alist method) :test #'equal)
slot-accessor-function)
(slot-value method 'optimized-function))))
;;;
;;; INITIALIZATION
;;;
;;; Error checking is done in before methods. Because of the simplicity of
;;; standard method objects the standard primary method can fill the slots.
;;;
;;; Methods are not reinitializable.
;;;
(defmethod reinitialize-instance ((method standard-method) &rest initargs)
(declare (ignore initargs))
(error "Attempt to reinitialize the method ~S.~%~
Method objects cannot be reinitialized."
method))
(defmethod legal-lambda-list-p ((object standard-method) x)
(lambda-list-legal-p x))
(defmethod legal-method-function-p ((object standard-method) x)
(if (or (null x) (functionp x))
t
"is not a function"))
(defmethod legal-qualifiers-p ((object standard-method) x)
(flet ((improper-list ()
(return-from legal-qualifiers-p "Is not a proper list.")))
(dolist-carefully (q x improper-list)
(let ((ok (legal-qualifier-p object q)))
(unless (eq ok t)
(return-from legal-qualifiers-p
(format nil "Contains ~S which ~A" q ok)))))
t))
(defmethod legal-qualifier-p ((object standard-method) x)
(if (and x (atom x))
t
"is not a non-null atom"))
(defmethod legal-slot-name-p ((object standard-method) x)
(cond ((not (symbolp x)) "is not a symbol and so cannot be bound")
((keywordp x) "is a keyword and so cannot be bound")
((memq x '(t nil)) "cannot be bound")
(t t)))
(defmethod legal-specializers-p ((object standard-method) x)
(flet ((improper-list ()
(return-from legal-specializers-p "Is not a proper list.")))
(dolist-carefully (s x improper-list)
(let ((ok (legal-specializer-p object s)))
(unless (eq ok t)
(return-from legal-specializers-p
(format nil "Contains ~S which ~A" s ok)))))
t))
(defvar *allow-experimental-specializers-p* nil)
(defmethod legal-specializer-p ((object standard-method) x)
(if (if *allow-experimental-specializers-p*
(specializerp x)
(or (classp x)
(eql-specializer-p x)))
t
"is neither a class object nor an eql specializer"))
(defmethod shared-initialize :before ((method standard-method)
slot-names
&key qualifiers
lambda-list
specializers
function
optimized-function
closure-generator
optimized-slot-indices
documentation)
(declare (ignore slot-names closure-generator optimized-slot-indices
documentation))
(flet ((lose (initarg value string)
(error "When initializing the method ~S:~%~
The ~S initialization argument was: ~S.~%~
which ~A."
method initarg value string)))
(let ((check-qualifiers (legal-qualifiers-p method qualifiers))
(check-lambda-list (legal-lambda-list-p method lambda-list))
(check-specializers (legal-specializers-p method specializers))
(check-function (legal-method-function-p method function))
(check-optimized-function
(legal-method-function-p method optimized-function)))
(unless (eq check-qualifiers t)
(lose :qualifiers qualifiers check-qualifiers))
(unless (eq check-lambda-list t)
(lose :lambda-list lambda-list check-lambda-list))
(unless (eq check-specializers t)
(lose :specializers specializers check-specializers))
(unless (eq check-optimized-function t)
(lose :optimized-function optimized-function check-optimized-function))
(unless (eq check-function t)
(lose :function function check-function)))))
(defmethod shared-initialize :before ((method standard-accessor-method)
slot-names
&key
(check-initargs-legality-p T)
slot-name
(slot-definition () slot-def-supplied-p))
(declare (ignore slot-names))
(when check-initargs-legality-p
(let ((legalp (legal-slot-name-p method slot-name)))
(unless (eq legalp t)
(error "The value of the :SLOT-NAME initarg ~A." legalp)))
(when (and slot-def-supplied-p
(not (*typep slot-definition 'direct-slot-definition)))
(error "When initializing the accessor method ~S:~%~
The initialization argument :slot-definition was ~A.~%~
It must be an instance of a subclass of DIRECT-SLOT-DEFINITION"
method slot-definition))))
(defmethod shared-initialize :after ((method standard-method)
slot-names
&key
function
optimized-function
closure-generator
identifier
(qualifiers () qualifiers-p)
(constant-value () constant-value-p))
(declare (ignore slot-names))
(when qualifiers-p
(setf (plist-value method 'qualifiers) qualifiers))
(when constant-value-p
(setf (plist-value method 'constant-value) constant-value))
(when function
(setf function (method-function-storage-form function))
(setf (slot-value method 'function) function)
(setf (method-function-method function) method)
(pushnew (cons nil function)
(method-cached-functions-alist method) :test #'equal))
(when optimized-function
(setf optimized-function (method-function-storage-form optimized-function))
(setf (slot-value method 'optimized-function) optimized-function)
(setf (method-function-method optimized-function) method)
(pushnew (cons nil optimized-function)
(method-cached-functions-alist method) :test #'equal))
(when closure-generator
(setf (slot-value method 'closure-generator)
(method-function-storage-form closure-generator)))
(when identifier
(setf (get-method-from-identifier identifier) method)))
(defmethod method-qualifiers ((method standard-method))
(plist-value method 'qualifiers))
(declaim (ftype (function (T) (values T boolean)) method-constant-value))
(defmethod method-constant-value ((method standard-method))
"First value returned is constant value returned by method if it does,
second value is whether or not the method has a constant value."
(let ((constant-value-or-default
(plist-value method 'constant-value 'no-constant-value)))
(if (eq constant-value-or-default 'no-constant-value)
(values NIL NIL)
(values constant-value-or-default T))))
(defclass generic-function (dependent-update-mixin
definition-source-mixin
documentation-mixin
metaobject)
()
(:metaclass funcallable-standard-class)
(:predicate-name generic-function-p))
(defclass standard-generic-function (generic-function)
((name
:initform nil
:initarg :name
:accessor generic-function-name)
(methods
:initform ()
:accessor generic-function-methods)
(method-class
:initarg :method-class
:accessor generic-function-method-class)
(method-combination
:initarg :method-combination
:accessor generic-function-method-combination)
(lambda-list
:initarg :lambda-list
:reader generic-function-lambda-list)
(argument-precedence-order
:initarg :argument-precedence-order
:reader generic-function-argument-precedence-order)
(declarations
:initform nil
:initarg :declarations
:reader generic-function-declarations)
; (permutation
; :accessor gf-permutation)
(arg-info
:initform (make-arg-info)
:reader gf-arg-info)
(dfun-state
:initform ()
:accessor gf-dfun-state)
(valid-p
:initform nil
:accessor gf-valid-p)
(pretty-arglist
:initform ()
:accessor gf-pretty-arglist)
)
(:metaclass funcallable-standard-class)
(:predicate-name standard-generic-function-p)
(:default-initargs :method-class *the-class-standard-method*
:method-combination *standard-method-combination*))
(defvar *the-class-generic-function* (find-class 'generic-function))
(defvar *the-class-standard-generic-function* (find-class 'standard-generic-function))
(defmethod store-method-function-p ((generic-function standard-generic-function)
(method standard-method)
initargs)
;; Should methods of this generic-function store their own method-function?
;; Answer is normally T to keep stay compatible with the AMOP even
;; though PCL actually uses the function in method-optimized-function
;; for efficiency. However, answer can be NIL if the programmer doesn't
;; care about method-functions, which will cut down on binary sizes
;; significantly since it would stop methods from carrying around
;; an extra (unused) method-function.
(declare (ignore initargs))
*standard-store-method-function-p*)
(defmethod store-method-optimized-function-p
((generic-function standard-generic-function)
(method standard-method)
initargs)
;; Should methods of this generic-function store their own
;; method-optimized-function?
;; Answer better be T unless a closure-generator is stored
;; for the method instead, or if the programmer has redefined the
;; discriminating method function dispatch code to use the
;; documented method-functions rather than the optimized PCL
;; method-optimized-functions,
(null (memq :optimized-slot-indices initargs)))
(defmethod store-closure-generator-p
((generic-function standard-generic-function)
(method standard-method)
initargs)
;; Should methods of this generic-function store their own
;; method function closure generators?
;; Answer better be T unless a method-optimized-function is
;; stored instead, or if the programmer has redefined the
;; the discriminating method function dispatch code to use
;; the documented method-functions rather than the optimized
;; PCL method-optimized-functions.
(not (null (memq :optimized-slot-indices initargs))))
(defmethod store-optimized-method-lambda-p
((generic-function standard-generic-function)
(method standard-method)
initargs)
;; Should methods of this generic-function store their own
;; their optimized-method-lambdas?
;; Generally only stored when the method contains slot-value
;; accesses on its parameter lists, in which case the lambda
;; is used to compile the cached method for slot accesses of non
;; :instance allocated slots or non-standard instances at
;; runtime to directly optimize those accesses.
(and *compile-slot-access-method-functions-at-runtime-p*
(not (null (memq :optimized-slot-indices initargs)))))
(declaim (ftype (function (T T T T) (values list list))
make-method-lambda
make-optimized-method-lambda))
(defmethod make-method-lambda ((generic-function standard-generic-function)
(method standard-method)
lambda-expression
environment)
(multiple-value-bind (optimized-method-lambda initargs)
(make-optimized-method-lambda generic-function method
lambda-expression environment)
;; Pass the optimized-method-lambda back through a global for
;; for macro or accessor expansion.
(setf *optimized-method-lambda* optimized-method-lambda)
(values
(make-documented-standard-method-lambda
lambda-expression
environment
*standard-pcl-make-method-lambda-doc-string*
(getf initargs :identifier))
initargs)))
(defmethod make-optimized-method-lambda
((generic-function standard-generic-function)
(method standard-method)
lambda-expression
environment)
(make-optimized-standard-method-lambda generic-function method
lambda-expression environment))
(defmethod make-closure-generator-form
((generic-function standard-generic-function)
(method standard-method)
optimized-method-lambda
initargs)
;; Closure generators must be in the form of functions whose arguments
;; is a list of the wrappers of the objects passed to a generic function
;; (or null), and which generates the method-optimized-function cached
;; for the generic function when the gf is called with parameters having
;; those wrappers.
(make-std-closure-generator-form
generic-function method optimized-method-lambda initargs))
(defmethod optimize-instance-access ((generic-function standard-generic-function)
(method standard-method)
(parameter-class T)
parameter slots read/write slot-name new-value)
(optimize-std-instance-access parameter-class parameter slots read/write
slot-name new-value))
(defmethod optimize-instance-access ((generic-function standard-generic-function)
(method standard-method)
(parameter-class structure-class)
parameter slots read/write slot-name new-value)
(declare (ignore slots))
(let ((slotd (find-slot-definition parameter-class slot-name)))
(ecase read/write
(:read
`(,(slot-definition-defstruct-accessor-symbol slotd) ,parameter))
(:write
`(setf (,(slot-definition-defstruct-accessor-symbol slotd) ,parameter)
,new-value))
(:boundp
'T))))
(defmethod reader-method-class ((class slot-class) direct-slot &rest initargs)
(declare (ignore direct-slot initargs))
*the-class-standard-reader-method*)
(defmethod writer-method-class ((class slot-class) direct-slot &rest initargs)
(declare (ignore direct-slot initargs))
*the-class-standard-writer-method*)
(defmethod boundp-method-class ((class slot-class) direct-slot &rest initargs)
(declare (ignore direct-slot initargs))
*the-class-standard-boundp-method*)
(defmethod reader-method-class ((class standard-class)
(direct-slot standard-direct-slot-definition)
&rest initargs)
(declare (ignore initargs))
*the-class-standard-reader-method*)
(defmethod writer-method-class ((class standard-class)
(direct-slot standard-direct-slot-definition)
&rest initargs)
(declare (ignore initargs))
*the-class-standard-writer-method*)
(defmethod boundp-method-class ((class standard-class)
(direct-slot standard-direct-slot-definition)
&rest initargs)
(declare (ignore initargs))
*the-class-standard-boundp-method*)
(defmethod reader-method-class ((class funcallable-standard-class)
(direct-slot standard-direct-slot-definition)
&rest initargs)
(declare (ignore initargs))
*the-class-standard-reader-method*)
(defmethod writer-method-class ((class funcallable-standard-class)
(direct-slot standard-direct-slot-definition)
&rest initargs)
(declare (ignore initargs))
*the-class-standard-writer-method*)
(defmethod boundp-method-class ((class funcallable-standard-class)
(direct-slot standard-direct-slot-definition)
&rest initargs)
(declare (ignore initargs))
*the-class-standard-boundp-method*)
(defmethod print-object ((generic-function generic-function) stream)
(named-object-print-function
generic-function
stream
(list (length (the list (generic-function-methods generic-function))))))
(defmethod shared-initialize :before
((generic-function standard-generic-function)
slot-names
&key (check-initargs-legality-p T)
(name nil namep)
(lambda-list () lambda-list-p)
argument-precedence-order
declarations
documentation
(method-class nil method-class-supplied-p)
(method-combination nil method-combination-supplied-p))
(declare (ignore slot-names documentation))
(when namep
(set-function-name generic-function name))
(flet ((initarg-error (initarg value string)
(error "When initializing the generic-function ~S:~%~
The ~S initialization argument was: ~A.~%~
It must be ~A."
generic-function initarg value string)))
(cond (method-class-supplied-p
(when (symbolp method-class)
(setq method-class (find-class method-class)))
(unless (and (classp method-class)
(*subtypep (class-eq-specializer method-class)
*the-class-method*))
(initarg-error :method-class
method-class
"a subclass of the class METHOD"))
(setf (slot-value generic-function 'method-class) method-class))
((slot-boundp generic-function 'method-class))
(check-initargs-legality-p
(initarg-error :method-class
"not supplied"
"a subclass of the class METHOD")))
(cond (method-combination-supplied-p
(unless (method-combination-p method-combination)
(initarg-error :method-combination
method-combination
"a method combination object")))
((slot-boundp generic-function 'method-combination))
(check-initargs-legality-p
(initarg-error :method-combination
"not supplied"
"a method combination object")))
(cond (lambda-list-p
(unless (legal-lambda-list-p generic-function lambda-list)
(initarg-error :lambda-list
lambda-list
"lambda list without default initial value forms"))
(when argument-precedence-order
(unless (and (listp argument-precedence-order)
(permutation-p argument-precedence-order
(lambda-list-required-args
lambda-list)))
(initarg-error :argument-precedence-order
argument-precedence-order
"permutation of required :lambda-list args"))))
(argument-precedence-order
(initarg-error :argument-precedence-order
argument-precedence-order
"supplied only when :lambda-list is supplied")))
(when (and declarations
(not (legal-declarations-p generic-function declarations)))
(initarg-error :declarations
declarations
"a legal set of DEFGENERIC declarations"))))
(defmethod shared-initialize :after
((generic-function standard-generic-function)
slot-names
&key (lambda-list () lambda-list-p)
(argument-precedence-order () argument-precedence-order-p))
(declare (ignore slot-names argument-precedence-order))
(when (and lambda-list-p (not argument-precedence-order-p))
(setf (slot-value generic-function 'argument-precedence-order)
(lambda-list-required-args lambda-list))))
(defmethod legal-declarations-p ((object standard-generic-function) x)
(listp x))
(defmethod legal-lambda-list-p ((object standard-generic-function) x)
(lambda-list-legal-p x NIL (remove '&aux lambda-list-keywords)))
#||
(defmethod reinitialize-instance ((generic-function standard-generic-function)
&rest initargs
&key name
lambda-list
argument-precedence-order
declarations
documentation
method-class
method-combination)
(declare (ignore documentation declarations argument-precedence-order
lambda-list name method-class method-combination))
(macrolet ((add-initarg (check name slot-name)
`(unless ,check
(push (slot-value generic-function ,slot-name) initargs)
(push ,name initargs))))
; (add-initarg name :name 'name)
; (add-initarg lambda-list :lambda-list 'lambda-list)
; (add-initarg argument-precedence-order
; :argument-precedence-order
; 'argument-precedence-order)
; (add-initarg declarations :declarations 'declarations)
; (add-initarg documentation :documentation 'documentation)
; (add-initarg method-class :method-class 'method-class)
; (add-initarg method-combination :method-combination 'method-combination)
(apply #'call-next-method generic-function initargs)))
||#
;;;
;;; These three are scheduled for demolition.
;;;
(defmethod remove-named-method (generic-function-name argument-specifiers
&optional extra)
(let ((generic-function ())
(method ()))
(cond ((or (null (fboundp generic-function-name))
(not (generic-function-p
(setq generic-function
(symbol-function generic-function-name)))))
(error "~S does not name a generic-function."
generic-function-name))
((null (setq method (get-method generic-function
extra
(parse-specializers
argument-specifiers)
nil)))
(error "There is no method for the generic-function ~S~%~
which matches the argument-specifiers ~S."
generic-function
argument-specifiers))
(t
(remove-method generic-function method)))))
(defvar *reinitialize-gf-updates-dfun-p* T)
(defun real-add-named-method (generic-function-name
method-class
qualifiers
specializers
lambda-list
function
optimized-function
closure-generator
&rest other-initargs)
;; What about changing the class of the generic-function if there is
;; one. Whose job is that anyways. Do we need something kind of
;; like class-for-redefinition?
(let* ((*reinitialize-gf-updates-dfun-p* NIL)
(generic-function
(ensure-generic-function generic-function-name
:lambda-list (method-ll->generic-function-ll lambda-list)))
(specs (parse-specializers specializers))
(new (apply #'make-instance
method-class
:qualifiers qualifiers
:specializers specs
:lambda-list lambda-list
:function function
:function
(method-function-storage-form function)
:optimized-function
(method-function-storage-form optimized-function)
:closure-generator
(method-function-storage-form closure-generator)
other-initargs)))
(add-method generic-function new)))
(defun make-specializable (function-name &key (arglist nil arglistp))
(declare (type boolean arglistp))
(cond ((not (null arglistp)))
((not (fboundp function-name)))
((fboundp 'function-arglist)
;; function-arglist exists, get the arglist from it.
(setq arglist (function-arglist function-name)))
(t
(error
"The :arglist argument to make-specializable was not supplied~%~
and there is no version of FUNCTION-ARGLIST defined for this~%~
port of Portable CommonLoops.~%~
You must either define a version of FUNCTION-ARGLIST (which~%~
should be easy), and send it off to the Portable CommonLoops~%~
people or you should call make-specializable again with the~%~
:arglist keyword to specify the arglist.")))
(let ((original (and (fboundp function-name)
(symbol-function function-name)))
(generic-function (make-instance 'standard-generic-function
:name function-name))
(nrequireds 0))
(declare (type index nrequireds))
(if (generic-function-p original)
original
(progn
(dolist (arg arglist)
(if (memq arg lambda-list-keywords)
(return)
(incf nrequireds)))
(setf (symbol-function function-name) generic-function)
(set-function-name generic-function function-name)
(when arglistp
(setf (gf-pretty-arglist generic-function) arglist))
(when original
(add-named-method
function-name
'standard-method
()
(make-list nrequireds :initial-element 't)
arglist
(when (call-store-method-function-p
generic-function
(class-prototype *the-class-standard-method*)
nil)
(make-std-documented-method-function original))
original
NIL))
generic-function))))
(defun real-get-method (generic-function qualifiers specializers
&optional (errorp t))
(let ((hit
(dolist (method (generic-function-methods generic-function))
(when (and (equal qualifiers (method-qualifiers method))
(every #'same-specializer-p specializers
(method-specializers method)))
(return method)))))
(cond (hit hit)
((null errorp) nil)
(t
(error "No method on ~S with qualifiers ~:S and specializers ~:S."
generic-function qualifiers specializers)))))
;;;
;;; Compute various information about a generic-function's arglist by looking
;;; at the argument lists of the methods. The hair for trying not to use
;;; &rest arguments lives here.
;;; The values returned are:
;;; number-of-required-arguments
;;; the number of required arguments to this generic-function's
;;; discriminating function
;;; &rest-argument-p
;;; whether or not this generic-function's discriminating
;;; function takes an &rest argument.
;;; specialized-argument-positions
;;; a list of the positions of the arguments this generic-function
;;; specializes (e.g. for a classical generic-function this is the
;;; list: (1)).
;;;
(declaim (ftype (function (T) (values index boolean list))
compute-discriminating-function-arglist-info))
(defmethod compute-discriminating-function-arglist-info
((generic-function standard-generic-function))
(declare (values number-of-required-arguments
rest-argument-p
specialized-argument-postions))
(let ((number-required nil)
(restp nil)
(specialized-positions ())
(methods (generic-function-methods generic-function)))
(declare (type boolean restp) (list specialized-positions methods))
(dolist (method methods)
(multiple-value-setq (number-required restp specialized-positions)
(compute-discriminating-function-arglist-info-internal
generic-function method number-required restp specialized-positions)))
(values (the index number-required) restp (sort specialized-positions #'<))))
(declaim (ftype (function (T T T T T) (values index boolean list))
compute-discriminating-function-arglist-info-internal))
(defun compute-discriminating-function-arglist-info-internal
(generic-function method number-of-requireds restp
specialized-argument-positions)
(declare (ignore generic-function) (type (or null fixnum) number-of-requireds))
(let ((requireds 0))
(declare (type index requireds))
;; Go through this methods arguments seeing how many are required,
;; and whether there is an &rest argument.
(dolist (arg (method-lambda-list method))
(cond ((eq arg '&aux) (return))
((memq arg '(&optional &rest &key))
(return (setq restp t)))
((memq arg lambda-list-keywords))
(t (setf requireds (the index (1+ requireds))))))
;; Now go through this method's type specifiers to see which
;; argument positions are type specified. Treat T specially
;; in the usual sort of way. For efficiency don't bother to
;; keep specialized-argument-positions sorted, rather depend
;; on our caller to do that.
(iterate ((type-spec (list-elements (method-specializers method)))
(pos (interval :from 0)))
(unless (eq type-spec *the-class-t*)
(pushnew pos specialized-argument-positions)))
;; Finally merge the values for this method into the values
;; for the exisiting methods and return them. Note that if
;; num-of-requireds is NIL it means this is the first method
;; and we depend on that.
(values (the index (if (and number-of-requireds
(< (the index number-of-requireds) requireds))
number-of-requireds
requireds))
(or restp
(and number-of-requireds
(/= (the index number-of-requireds) requireds)))
specialized-argument-positions)))
(defun make-discriminating-function-arglist (number-required-arguments restp)
(nconc (gathering ((args (collecting)))
(iterate ((i (interval :from 0 :below number-required-arguments)))
(gather (intern (format nil "Discriminating Function Arg ~D" i))
args)))
(when restp
`(&rest ,(intern "Discriminating Function &rest Arg")))))
;;;
;;;
;;;
(defstruct (arg-info
(:conc-name nil)
(:constructor make-arg-info ()))
arg-info-precedence
arg-info-metatypes
(arg-info-number-optional nil :type (or index null))
(arg-info-key/rest-p nil :type boolean)
arg-info-keywords ;nil no keyword or rest allowed
;(k1 k2 ..) each method must accept these keyword arguments
;T must have &key or &rest
gf-info-simple-accessor-type ; nil, reader, writer, boundp
(gf-precompute-dfun-and-emf-p nil :type boolean) ; set by set-arg-info
gf-info-static-c-a-m-emf
(gf-info-c-a-m-emf-std-p nil :type boolean)
(arg-info-lambda-list :no-lambda-list))
(declaim (ftype (function (T) boolean) arg-info-valid-p))
(defun arg-info-valid-p (arg-info)
(not (null (arg-info-number-optional arg-info))))
(declaim (ftype (function (T) boolean) arg-info-applyp))
(defun arg-info-applyp (arg-info)
(or (plusp (the index (arg-info-number-optional arg-info)))
(arg-info-key/rest-p arg-info)))
(declaim (ftype (function (T) index) arg-info-number-required))
(defun arg-info-number-required (arg-info)
(length (the list (arg-info-metatypes arg-info))))
(declaim (ftype (function (T) index) arg-info-nkeys))
(defun arg-info-nkeys (arg-info)
(count-if #'(lambda (x) (neq x 't)) (arg-info-metatypes arg-info)))
(defun set-arg-info (gf precedence metatypes number-optional key/rest-p keywords
&optional (lambda-list nil lambda-list-p))
(declare (type index number-optional)
(type boolean key/rest-p lambda-list-p))
(let ((arg-info (gf-arg-info gf)))
(setf (arg-info-precedence arg-info) precedence)
(setf (arg-info-metatypes arg-info) metatypes)
(setf (arg-info-number-optional arg-info) number-optional)
(setf (arg-info-key/rest-p arg-info) key/rest-p)
(setf (arg-info-keywords arg-info) keywords)
(when lambda-list-p
(setf (arg-info-lambda-list arg-info) lambda-list))
(setf (gf-precompute-dfun-and-emf-p arg-info)
(the boolean
(let* ((name (generic-function-name gf))
(sym (if (atom name) name (cadr name)))
(pkg-list (cons *the-pcl-package*
(package-use-list *the-pcl-package*))))
(not (null (memq (symbol-package sym) pkg-list))))))
arg-info))
(defun new-arg-info-from-generic-function (gf lambda-list argument-precedence-order)
(multiple-value-bind (nreq nopt keysp restp allow-other-keys-p keywords)
(analyze-lambda-list lambda-list)
(declare (type index nreq nopt)
(type boolean keysp restp)
(type list keywords)
(ignore allow-other-keys-p))
(let ((metatypes (make-list nreq))
(precedence
(compute-precedence lambda-list nreq argument-precedence-order)))
(set-arg-info gf
precedence
metatypes
nopt
(or keysp restp)
keywords
lambda-list))))
(defun new-arg-info-from-method (gf method)
(multiple-value-bind (nreq nopt keysp restp)
(analyze-lambda-list (method-lambda-list method))
(declare (type index nreq nopt)
(type boolean keysp restp))
(set-arg-info gf
(compute-precedence (method-lambda-list method) nreq ())
(mapcar #'raise-metatype
(make-list nreq) (method-specializers method))
nopt
(or keysp restp)
())))
(defun add-arg-info (generic-function method)
(let ((arg-info (gf-arg-info generic-function)))
(if (not (arg-info-valid-p arg-info))
(new-arg-info-from-method generic-function method)
(flet ((lose (string &rest args)
(error "Attempt to add the method ~S to the generic function ~S.~%~
But ~A"
method
generic-function
(apply #'format nil string args)))
(compare (x y)
(declare (type index x y))
(if (> x y) "more" "fewer")))
(multiple-value-bind (nreq nopt keysp restp allow-other-keys-p keywords)
(analyze-lambda-list (method-lambda-list method))
(declare (type index nreq nopt)
(type boolean keysp restp allow-other-keys-p)
(type list keywords))
(let ((gf-nreq (arg-info-number-required arg-info))
(gf-nopt (arg-info-number-optional arg-info))
(gf-key/rest-p (arg-info-key/rest-p arg-info))
(gf-keywords (arg-info-keywords arg-info)))
(declare (type index gf-nreq gf-nopt)
(type boolean gf-key/rest-p))
(unless (= nreq gf-nreq)
(lose "the method has ~A required arguments than the generic function."
(compare nreq gf-nreq)))
(unless (= nopt gf-nopt)
(lose "the method has ~S optional arguments than the generic function."
(compare nopt gf-nopt)))
(unless (eq (or keysp restp) gf-key/rest-p)
(error "the method and generic function differ in whether they accept~%~
rest or keyword arguments."))
(when gf-keywords
(unless (or (and restp (not keysp))
allow-other-keys-p
(every #'(lambda (k) (memq k keywords)) gf-keywords))
(error
"the generic function requires each method to accept the keyword arguments~%~
~S. The method does not accept all of these."
gf-keywords)))
(set-arg-info generic-function
(arg-info-precedence arg-info)
(mapcar #'raise-metatype (arg-info-metatypes arg-info)
(method-specializers method))
gf-nopt
gf-key/rest-p
gf-keywords)))))))
(defun remove-arg-info (generic-function method)
(let ((arg-info (gf-arg-info generic-function)))
(set-arg-info generic-function
(arg-info-precedence arg-info)
(let* ((nreq (arg-info-number-required arg-info))
(metatypes (make-list nreq))
(old-methods (generic-function-methods generic-function))
(methods (remove method old-methods)))
(declare (type index nreq))
(dolist (specls (mapcar #'method-specializers methods))
(setq metatypes (mapcar #'raise-metatype metatypes specls)))
metatypes)
(arg-info-number-optional arg-info)
(arg-info-key/rest-p arg-info)
(arg-info-keywords arg-info))))
(defmethod initialize-instance :after ((gf standard-generic-function)
&key (lambda-list nil lambda-list-p)
argument-precedence-order)
(when lambda-list-p
(new-arg-info-from-generic-function gf lambda-list argument-precedence-order))
(when (arg-info-valid-p (gf-arg-info gf))
(update-dfun gf)))
(defmethod reinitialize-instance :after ((gf standard-generic-function)
&rest args
&key (lambda-list nil lambda-list-p)
argument-precedence-order)
(let* ((arg-info (gf-arg-info gf))
(valid-p (arg-info-valid-p arg-info)))
(when lambda-list-p
(if (not valid-p)
(new-arg-info-from-generic-function gf lambda-list argument-precedence-order)
(setf (arg-info-lambda-list arg-info) lambda-list)))
(when (and *reinitialize-gf-updates-dfun-p*
valid-p args
(or (not (eq (car args) 'lambda-list))
(cddr args)))
(update-dfun gf))))
;;;
;;;
;;;
(defun compute-precedence (lambda-list nreq argument-precedence-order)
(declare (type list lambda-list argument-precedence-order))
(declare (ignore nreq))
(let ((nreq (analyze-lambda-list lambda-list)))
(declare (type index nreq))
(if (null argument-precedence-order)
(let ((list nil))(dotimes (i nreq list) (push (- (1- nreq) i) list)))
(mapcar #'(lambda (x) (position x lambda-list)) argument-precedence-order))))
(defmethod no-applicable-method (generic-function &rest args)
(cerror "Retry call to ~S"
"No matching method for the generic-function ~S,~@
when called with arguments ~S."
generic-function args)
(apply generic-function args))
(defmethod no-next-method ((generic-function standard-generic-function)
(method standard-method)
&rest args)
(error
"No next method for generic function ~S in method ~S"
generic-function method args))
(proclaim '(special *lazy-dfun-compute-p*))
(defun real-add-method (generic-function method)
(if (method-generic-function method)
(error "The method ~S is already part of the generic~@
function ~S. It can't be added to another generic~@
function until it is removed from the first one."
method (method-generic-function method))
(let* ((name (generic-function-name generic-function))
(qualifiers (method-qualifiers method))
(lambda-list (method-lambda-list method))
(specializers (method-specializers method))
(existing (get-method generic-function qualifiers specializers nil)))
;;
;; If there is already a method like this one then we must
;; get rid of it before proceeding. Note that we call the
;; generic function remove-method to remove it rather than
;; doing it in some internal way.
;;
(when existing (remove-method generic-function existing))
;;
(setf (method-generic-function method) generic-function)
(setf (method-function-name method)
(intern-function-name
(make-method-spec name
qualifiers
(unparse-specializers specializers))))
(unless (slot-boundp generic-function 'lambda-list)
(let ((gf-lambda-list
(method-ll->generic-function-ll lambda-list)))
(fast-set-slot-value generic-function 'lambda-list gf-lambda-list
slow-slot-value)
(fast-set-slot-value generic-function 'argument-precedence-order
(lambda-list-required-args gf-lambda-list)
slow-slot-value)))
(add-arg-info generic-function method)
(pushnew method (generic-function-methods generic-function))
(dolist (specializer specializers)
(add-direct-method specializer method))
(update-gf-info generic-function)
(update-dfun generic-function)
(maybe-update-constructors generic-function method)
method)))
(defun real-remove-method (generic-function method)
(if (neq generic-function (method-generic-function method))
(error "The method ~S is attached to the generic function~@
~S. It can't be removed from the generic function~@
to which it is not attached."
method (method-generic-function method))
(let* ((methods (generic-function-methods generic-function))
(new-methods (remove method methods)))
(setf (method-generic-function method) nil)
(setf (generic-function-methods generic-function) new-methods)
(dolist (specializer (method-specializers method))
(remove-direct-method specializer method))
(remove-arg-info generic-function method)
(update-dfun generic-function)
(maybe-update-constructors generic-function method)
generic-function)))
;;;
;;;
;;;
(declaim (ftype (function (T T) (values boolean boolean))
specializer-applicable-using-type-p))
(defun compute-applicable-methods-function (generic-function arguments)
(values (compute-applicable-methods-using-types
generic-function
(types-from-arguments generic-function arguments 'eql))))
(defmethod compute-applicable-methods
((generic-function generic-function) arguments)
(values (compute-applicable-methods-using-types
generic-function
(types-from-arguments generic-function arguments 'eql))))
(declaim (ftype (function (T T) (values T boolean))
compute-applicable-methods-using-classes))
(defmethod compute-applicable-methods-using-classes
((generic-function generic-function) classes)
(compute-applicable-methods-using-types
generic-function
(types-from-arguments generic-function classes 'class-eq)))
(declaim (ftype (function (T T) (values list boolean))
compute-applicable-methods-using-types))
(defun compute-applicable-methods-using-types (generic-function types)
(let ((definite-p t) (possibly-applicable-methods nil))
(declare (type boolean definite-p))
(dolist (method (generic-function-methods generic-function))
(let ((specls (method-specializers method))
(types types)
(possibly-applicable-p t) (applicable-p t))
(declare (type boolean possibly-applicable-p applicable-p))
(dolist (specl specls)
(multiple-value-bind (specl-applicable-p specl-possibly-applicable-p)
(specializer-applicable-using-type-p specl (pop types))
(declare (type boolean specl-applicable-p
specl-possibly-applicable-p))
(unless specl-applicable-p
(setq applicable-p nil))
(unless specl-possibly-applicable-p
(setq possibly-applicable-p nil)
(return nil))))
(when possibly-applicable-p
(unless applicable-p (setq definite-p nil))
(push method possibly-applicable-methods))))
(values (sort-applicable-methods generic-function
(nreverse possibly-applicable-methods)
types)
definite-p)))
;This is used to speed up precompute-effective-methods.
(defvar *in-precompute-effective-methods-p* nil)
;used only in specializer-applicable-using-type-p
(declaim (ftype (function (T T) (values boolean boolean))
class-applicable-using-class-p))
(defun class-applicable-using-class-p (specl type)
(let ((pred (class-on-class-precedence-list-p specl type)))
(declare (type boolean pred))
(values pred
(or pred
(if (not *in-precompute-effective-methods-p*)
;; classes might get common subclass
(superclasses-compatible-p specl type)
;; worry only about existing classes
(classes-have-common-subclass-p specl type))))))
;used only in map-all-orders
(declaim (ftype (function (T T) boolean) class-might-precede-p))
(defun class-might-precede-p (class1 class2)
(if (not *in-precompute-effective-methods-p*)
(not (or (eq class1 class2)
(class-on-class-precedence-list-p class1 class2)))
(class-can-precede-p class1 class2)))
(declaim (ftype (function (T T) (values boolean boolean)) saut-and-p))
(defun saut-and-p (specl type)
(let ((applicable-p nil)
(all-solid-p T))
(declare (type boolean applicable-p all-solid-p))
(dolist (type (cdr type))
(multiple-value-bind (appl poss-appl)
(specializer-applicable-using-type-p specl type)
(declare (type boolean appl poss-appl))
(when appl (return (setq applicable-p t)))
(unless poss-appl (setq all-solid-p nil))))
(values applicable-p (or applicable-p all-solid-p))))
(declaim (ftype (function (T T) (values boolean boolean)) saut-or-p))
(defun saut-or-p (specl type)
;; T T if all are definitely applicable.
;; If any are definitely unapplicable, then definitely unapplicable.
;; Else NIL NIL.
(let ((any-unapplicable-p NIL)
(definitely-unapplicable-p NIL))
(declare (type boolean any-unapplicable-p definitely-unapplicable-p))
(dolist (type (cdr type))
(multiple-value-bind (appl poss-appl)
(specializer-applicable-using-type-p specl type)
(declare (type boolean appl poss-appl))
(unless appl
(setq any-unapplicable-p t)
(when poss-appl
(return (setq definitely-unapplicable-p T))))))
(values (not any-unapplicable-p)
(or (not any-unapplicable-p) definitely-unapplicable-p))))
(declaim (ftype (function (T T) (values boolean boolean)) saut-not-p))
(defun saut-not-p (specl type)
(let ((ntype (cadr type)))
(values nil
(the boolean
(case (car ntype)
(class (saut-not-class-p specl ntype))
(class-eq (saut-not-class-eq-p specl ntype))
(eql (saut-not-eql-p specl ntype))
(t (error "~s cannot handle the second argument ~s"
'specializer-applicable-using-type-p type)))))))
(declaim (ftype (function (T T) boolean) saut-not-class-p))
(defun saut-not-class-p (specl ntype)
(not (class-on-class-precedence-list-p (cadr ntype) (type-class specl))))
(declaim (ftype (function (T T) boolean) saut-not-class-eq-p))
(defun saut-not-class-eq-p (specl ntype)
(let ((class (case (car specl)
(eql (class-of (cadr specl)))
(class-eq (cadr specl)))))
(not (eq class (cadr ntype)))))
(declaim (ftype (function (T T) boolean) saut-not-class-eql-p))
(defun saut-not-eql-p (specl ntype)
(case (car specl)
(eql (not (eql (cadr specl) (cadr ntype))))
(t t)))
(declaim (ftype (function (T T) (values boolean boolean)) saut-class-p))
(defun saut-class-p (specl type)
(case (car specl)
(class (class-applicable-using-class-p (cadr specl) (cadr type)))
(t (values
nil
(class-on-class-precedence-list-p (cadr type) (type-class specl))))))
(declaim (ftype (function (T T) (values boolean boolean)) saut-eq-p))
(defun saut-class-eq-p (specl type)
(if (eq (car specl) 'eql)
(values nil (eq (class-of (cadr specl)) (cadr type)))
(let ((pred (case (car specl)
(class-eq (eq (cadr specl) (cadr type)))
(class (or (eq (cadr specl) (cadr type))
(class-on-class-precedence-list-p
(cadr specl) (cadr type)))))))
(declare (type boolean pred))
(values pred pred))))
(declaim (ftype (function (T T) (values boolean boolean)) saut-eql-p))
(defun saut-eql-p (specl type)
(let ((pred
(case (car specl)
(eql (eql (cadr specl) (cadr type)))
(class-eq (eq (cadr specl) (class-of (cadr type))))
(class (class-on-class-precedence-list-p
(cadr specl) (class-of (cadr type)))))))
(declare (type boolean pred))
(values pred pred)))
(defun specializer-applicable-using-type-p (specl type)
(declare (values applicable-p maybe-applicable-p))
(setq specl (type-from-specializer specl))
(when (eq specl 't)
(return-from specializer-applicable-using-type-p (values t t)))
;; This is used by c-a-m-u-t and generate-discrimination-net-internal,
;; and has only what they need.
;; If it can't handle it, then it gives up and hopes the normal
;; subtypep can figure it out.
(if (or (atom type) (eq (car type) 't))
(values nil t)
(case (car type)
(and (saut-and-p specl type))
(or (saut-or-p specl type))
(not (saut-not-p specl type))
(class (saut-class-p specl type))
(class-eq (saut-class-eq-p specl type))
(eql (saut-eql-p specl type))
(t (multiple-value-bind (appl certain-p)
(subtypep (convert-to-system-type type)
(convert-to-system-type specl))
(declare (type boolean appl certain-p))
(values appl (or appl (not certain-p))))))))
(defun proclaim-incompatible-superclasses (classes)
(setq classes (mapcar #'(lambda (class)
(if (symbolp class)
(find-class class)
class))
classes))
(dolist (class classes)
(dolist (other-class classes)
(unless (eq class other-class)
(pushnew other-class (class-incompatible-superclass-list class))))))
(declaim (ftype (function (T T) boolean) superclasses-compatible-p))
(defun superclasses-compatible-p (class1 class2)
(assure-finalized class1)
(assure-finalized class2)
(let ((cpl1 (class-precedence-list class1))
(cpl2 (class-precedence-list class2)))
(dolist (sc1 cpl1 t)
(dolist (ic (class-incompatible-superclass-list sc1))
(when (memq ic cpl2)
(return-from superclasses-compatible-p nil))))))
(mapc
#'proclaim-incompatible-superclasses
'(;; superclass class
(built-in-class std-class structure-class) ; direct subclasses of pcl-class
(standard-class funcallable-standard-class)
;; superclass metaobject
(class eql-specializer class-eq-specializer method ; method-combination
generic-function slot-definition)
;; metaclass built-in-class
(number sequence character ; direct subclasses of t, but not array
standard-object structure-object)
(number array character symbol ; direct subclasses of t, but not sequence
standard-object structure-object)
(complex float rational) ; direct subclasses of number
(integer ratio) ; direct subclasses of rational
(list vector) ; direct subclasses of sequence
(cons null) ; direct subclasses of list
(string bit-vector) ; direct subclasses of vector
))
(declaim (ftype (function (T T) boolean) classes-have-common-subclass-p))
(defun classes-have-common-subclass-p (class1 class2)
(or (eq class1 class2)
(let ((class1-subs (class-direct-subclasses class1)))
(or (not (null (memq class2 class1-subs)))
(dolist (class1-sub class1-subs nil)
(when (classes-have-common-subclass-p class1-sub class2)
(return t)))))))
(defun order-specializers (specl1 specl2 index compare-classes-function)
(declare (type real-function compare-classes-function))
(let ((type1 (specializer-type specl1))
(type2 (specializer-type specl2)))
(cond ((eq specl1 specl2)
nil)
((atom type1)
specl2)
((atom type2)
specl1)
(t
(case (car type1)
(class (case (car type2)
(class (funcall compare-classes-function specl1 specl2 index))
(t specl2)))
(class-eq (case (car type2)
(eql specl2)
(class-eq nil)
(class type1)))
(eql (case (car type2)
(eql nil)
(t specl1))))))))
(defun sort-applicable-methods (generic-function methods types)
(sort-methods methods
(arg-info-precedence (gf-arg-info generic-function))
#'(lambda (class1 class2 index)
(declare (type index index))
(let* ((class (type-class (nth index types)))
(cpl (class-precedence-list class)))
(if (memq class2 (memq class1 cpl))
class1 class2)))))
(defun sort-methods (methods precedence compare-classes-function)
(flet ((sorter (method1 method2)
(dolist (index precedence)
(declare (type index index))
(let* ((specl1 (nth index (method-specializers method1)))
(specl2 (nth index (method-specializers method2)))
(order (order-specializers
specl1 specl2 index compare-classes-function)))
(when order
(return (eq order specl1)))))))
(stable-sort methods #'sorter)))
(defun map-all-orders (methods precedence function)
(declare (type real-function function))
(let ((choices nil))
(flet ((compare-classes-function (class1 class2 index)
(declare (ignore index))
(let ((choice nil))
(dolist (c choices nil)
(when (or (and (eq (first c) class1)
(eq (second c) class2))
(and (eq (first c) class2)
(eq (second c) class1)))
(return (setq choice c))))
(unless choice
(setq choice
(if (class-might-precede-p class1 class2)
(if (class-might-precede-p class2 class1)
(list class1 class2 nil t)
(list class1 class2 t))
(if (class-might-precede-p class2 class1)
(list class2 class1 t)
(let ((name1 (class-name class1))
(name2 (class-name class2)))
(if (and name1 name2 (symbolp name1) (symbolp name2)
(string< (symbol-name name1)
(symbol-name name2)))
(list class1 class2 t)
(list class2 class1 t))))))
(push choice choices))
(car choice))))
(loop (funcall function
(sort-methods methods precedence #'compare-classes-function))
(unless (dolist (c choices nil)
(unless (third c)
(rotatef (car c) (cadr c))
(return (setf (third c) t))))
(return nil))))))
(defmethod same-specializer-p ((specl1 specializer) (specl2 specializer))
nil)
(defmethod same-specializer-p ((specl1 class) (specl2 class))
(eq specl1 specl2))
(defmethod specializer-class ((specializer class))
specializer)
(defmethod same-specializer-p ((specl1 class-eq-specializer)
(specl2 class-eq-specializer))
(eq (specializer-class specl1) (specializer-class specl2)))
(defmethod same-specializer-p ((specl1 eql-specializer)
(specl2 eql-specializer))
(eq (specializer-object specl1) (specializer-object specl2)))
(defmethod specializer-class ((specializer eql-specializer))
(class-of (slot-value specializer 'object)))
(defvar *in-gf-arg-info-p* nil)
(defvar arg-info-reader (make-std-reader-method-function 'arg-info))
(defun types-from-arguments (generic-function arguments &optional type-modifier)
(let* ((arg-info (if *in-gf-arg-info-p*
(method-function-funcall arg-info-reader generic-function)
(let ((*in-gf-arg-info-p* t))
(gf-arg-info generic-function))))
(metatypes (arg-info-metatypes arg-info))
(types-rev nil))
(declare (type list metatypes))
(dolist (mt metatypes)
#-(or excl kcl)
(declare (ignore mt))
(unless arguments
(error "The function ~S requires at least ~D arguments"
(generic-function-name generic-function)
(length metatypes)))
(let ((arg (pop arguments)))
(push (if type-modifier `(,type-modifier ,arg) arg) types-rev)))
(values (nreverse types-rev) arg-info)))
(defun get-wrappers-from-classes (nkeys wrappers classes metatypes)
(declare (type index nkeys))
(let* ((w wrappers) (w-tail w) (mt-tail metatypes))
(dolist (class (if (listp classes) classes (list classes)))
(unless (eq 't (car mt-tail))
(let ((c-w (class-wrapper class)))
(unless c-w (return-from get-wrappers-from-classes nil))
(if (= nkeys 1)
(setq w c-w)
(setf (car w-tail) c-w
w-tail (cdr w-tail)))))
(setq mt-tail (cdr mt-tail)))
w))
(declaim (ftype (function (T T T) (values T T)) accessor-values))
(defun accessor-values (arg-info classes methods)
(let* ((accessor-type (gf-info-simple-accessor-type arg-info))
(accessor-class (case accessor-type
(reader (car classes))
(writer (cadr classes))
(boundp (car classes))))
(slot-name (and accessor-class
(class-standard-p accessor-class)
(accessor-method-slot-name (car methods))))
(slotd (and accessor-class
(find-slot-definition accessor-class slot-name))))
(if (and slotd (slot-accessor-std-p slotd accessor-type))
(values accessor-type (slot-definition-location slotd))
(values nil nil))))
;;;
;;; Given a generic function and a set of arguments to that generic function,
;;; returns a mess of values.
;;;
;;; <function> The compiled effective method function for this set of
;;; arguments.
;;;
;;; <applicable> Sorted list of applicable methods.
;;;
;;; <wrappers> Is a single wrapper if the generic function has only
;;; one key, that is arg-info-nkeys of the arg-info is 1.
;;; Otherwise a list of the wrappers of the specialized
;;; arguments to the generic function.
;;;
;;; Note that all these wrappers are valid. This function
;;; does invalid wrapper traps when it finds an invalid
;;; wrapper and then returns the new, valid wrapper.
;;;
;;; <invalidp> True if any of the specialized arguments had an invalid
;;; wrapper, false otherwise.
;;;
;;; <type> READER or WRITER when the only method that would be run
;;; is a standard reader or writer method. To be specific,
;;; the value is READER when the method combination is eq to
;;; *standard-method-combination*; there are no applicable
;;; :before, :after or :around methods; and the most specific
;;; primary method is a standard reader method.
;;;
;;; <index> If <type> is READER or WRITER, and the slot accessed is
;;; an :instance slot, this is the index number of that slot
;;; in the object argument.
;;;
(declaim (ftype (function (T T T)
(values T boolean T T T T))
cache-miss-values))
(defun cache-miss-values (gf args state)
(let* ((arg-info (method-function-funcall arg-info-reader gf))
(metatypes (arg-info-metatypes arg-info))
(for-accessor-p (eq state 'accessor))
(for-cache-p (or (eq state 'caching) (eq state 'accessor)))
(cam-std-p (gf-info-c-a-m-emf-std-p arg-info))
(args-tail args) (invalid-wrapper-p nil)
(wrappers-rev nil) (dfun-wrappers-rev nil)
(types-rev nil) (classes-rev nil))
(declare (type list metatypes)
(type boolean for-accessor-p for-cache-p cam-std-p
invalid-wrapper-p))
(dolist (mt metatypes)
(unless args-tail
(error "The function ~S requires at least ~D arguments"
(generic-function-name gf) (length metatypes)))
(let ((arg (pop args-tail)))
(multiple-value-bind (wrapper class type)
(if (eq mt 't)
(values nil *the-class-t* 't)
(let ((wrapper (wrapper-of arg)))
(when (invalid-wrapper-p wrapper)
(setq invalid-wrapper-p t)
(setq wrapper (fast-check-wrapper-validity arg)))
(push wrapper dfun-wrappers-rev)
(let ((wclass (wrapper-class wrapper)))
(values wrapper wclass `(class-eq ,wclass)))))
(push wrapper wrappers-rev)
(push class classes-rev)
(push type types-rev))))
(let* ((wrappers (nreverse wrappers-rev))
(classes (nreverse classes-rev))
(types (mapcar #'(lambda (class) `(class-eq ,class)) classes)))
(multiple-value-bind (methods all-applicable-and-sorted-p)
(if cam-std-p
(compute-applicable-methods-using-types gf types)
(compute-applicable-methods-using-classes gf classes))
(declare (type boolean all-applicable-and-sorted-p))
(let ((sdfun (if (or all-applicable-and-sorted-p cam-std-p)
(funcall-function
(get-secondary-dispatch-function1
gf methods types
all-applicable-and-sorted-p)
nil
(and for-cache-p wrappers))
(default-secondary-dispatch-function gf))))
(multiple-value-bind (accessor-type index)
(if (and for-accessor-p all-applicable-and-sorted-p methods)
(accessor-values arg-info classes methods)
(values nil nil))
(values (if (and dfun-wrappers-rev (null (cdr dfun-wrappers-rev)))
(car dfun-wrappers-rev)
(nreverse dfun-wrappers-rev))
invalid-wrapper-p
sdfun methods accessor-type index)))))))
(defun sdfun-for-caching (gf classes)
(let ((types (mapcar #'class-eq-type classes)))
(multiple-value-bind (methods all-applicable-and-sorted-p)
(compute-applicable-methods-using-types gf types)
(method-function-funcall
(get-secondary-dispatch-function1
gf methods types all-applicable-and-sorted-p)
nil (mapcar #'class-wrapper classes)))))
(defun value-for-caching (gf classes)
(let ((methods (compute-applicable-methods-using-types
gf (mapcar #'class-eq-type classes))))
(method-constant-value (car methods))))
(defun default-secondary-dispatch-function (generic-function)
#'(lambda (&rest args)
(let ((methods (compute-applicable-methods generic-function args)))
(if methods
(method-function-apply
(get-effective-method-function generic-function methods)
args)
(apply #'no-applicable-method generic-function args)))))
(defun list-eq (x y)
(loop (when (atom x) (return (eq x y)))
(when (atom y) (return nil))
(unless (eq (car x) (car y)) (return nil))
(setq x (cdr x) y (cdr y))))
(defvar *std-cam-methods* nil)
(declaim (ftype (function (T) (values T boolean))
compute-applicable-methods-emf))
(defun compute-applicable-methods-emf (generic-function)
(if (eq *boot-state* 'complete)
(let* ((cam (gdefinition 'compute-applicable-methods))
(cam-methods (compute-applicable-methods-using-types
cam (list `(eql ,generic-function) t))))
(values (get-effective-method-function cam cam-methods)
(list-eq cam-methods
(or *std-cam-methods*
(setq *std-cam-methods*
(compute-applicable-methods-using-types
cam (list `(eql ,cam) t)))))))
(values #'compute-applicable-methods-function t)))
(declaim (ftype (function (T) boolean) compute-applicable-methods-emf-std-p))
(defun compute-applicable-methods-emf-std-p (gf)
(gf-info-c-a-m-emf-std-p (gf-arg-info gf)))
(defvar *old-c-a-m-gf-methods* nil)
(defun update-all-c-a-m-gf-info (c-a-m-gf)
(let ((methods (generic-function-methods c-a-m-gf)))
(if (every #'(lambda (old-method)
(memq old-method methods))
*old-c-a-m-gf-methods*)
(let ((gfs-to-do nil)
(gf-classes-to-do nil))
(dolist (method methods)
(unless (memq method *old-c-a-m-gf-methods*)
(let ((specl (car (method-specializers method))))
(if (eql-specializer-p specl)
(pushnew (specializer-object specl) gfs-to-do)
(pushnew (specializer-class specl) gf-classes-to-do)))))
(map-all-generic-functions
#'(lambda (gf)
(when (or (memq gf gfs-to-do)
(let ((cpl (wrapper-class-precedence-list
(fast-wrapper-of gf))))
(dolist (class gf-classes-to-do nil)
(if (memq class cpl) (return T)))))
(update-c-a-m-gf-info gf)))))
(map-all-generic-functions #'update-c-a-m-gf-info))
(setq *old-c-a-m-gf-methods* methods)))
(defun update-gf-info (gf)
(update-c-a-m-gf-info gf)
(update-gf-simple-accessor-type gf))
(defun update-c-a-m-gf-info (gf)
(multiple-value-bind (c-a-m-emf std-p)
(compute-applicable-methods-emf gf)
(declare (type boolean std-p))
(let ((arg-info (gf-arg-info gf)))
(setf (gf-info-static-c-a-m-emf arg-info) c-a-m-emf)
(setf (gf-info-c-a-m-emf-std-p arg-info) std-p))))
(defun update-gf-simple-accessor-type (gf)
(let ((arg-info (gf-arg-info gf)))
(setf (gf-info-simple-accessor-type arg-info)
(let* ((methods (generic-function-methods gf))
(class (and methods (class-of (car methods))))
(type (and class
(cond ((eq class *the-class-standard-reader-method*)
'reader)
((eq class *the-class-standard-writer-method*)
'writer)
((eq class *the-class-standard-boundp-method*)
'boundp)))))
(when (and (gf-info-c-a-m-emf-std-p arg-info)
type
(dolist (method (cdr methods) t)
(unless (eq class (class-of method)) (return nil)))
(eq (generic-function-method-combination gf)
*standard-method-combination*))
type)))))
(declaim (ftype (function (T T) boolean)
accessor-methods-safe-to-use-slot-wrapper-optimizations-p))
(defun accessor-methods-safe-to-use-slot-wrapper-optimizations-p
(methods type)
;; Returns whether all accessor-methods Methods are safe for the slot-value
;; wrapper optimizations.
(let ((safe-specializers
(ecase type
(reader *safe-slot-value-using-class-specializers*)
(writer *safe-set-slot-value-using-class-specializers*)
(boundp *safe-slot-boundp-using-class-specializers*))))
(declare (type list safe-specializers))
(dolist (method methods T)
(unless (member (mapcar #'class-name (method-specializers method))
safe-specializers :test #'equal)
(return NIL)))))
(declaim (ftype (function (T T) boolean) slot-values-safe-using-class-p))
(defun slot-values-safe-using-class-p (class slotd)
(let ((types1 (list class (class-prototype class) slotd)))
(and (or *safe-to-use-slot-value-wrapper-optimizations-p*
(accessor-methods-safe-to-use-slot-wrapper-optimizations-p
(compute-applicable-methods #'slot-value-using-class types1)
'reader))
(or *safe-to-use-set-slot-value-wrapper-optimizations-p*
(accessor-methods-safe-to-use-slot-wrapper-optimizations-p
(compute-applicable-methods
(gdefinition '(setf slot-value-using-class)) (cons T types1))
'writer))
(or *safe-to-use-slot-boundp-wrapper-optimizations-p*
(accessor-methods-safe-to-use-slot-wrapper-optimizations-p
(compute-applicable-methods #'slot-boundp-using-class types1)
'boundp)))))
(declaim (ftype (function (T T T T) (values function boolean))
get-accessor-method-function))
(defun get-accessor-method-function (gf type class slotd)
(let* ((types1 `((eql ,class) (class-eq ,class) (eql ,slotd)))
(types (if (eq type 'writer) `(t ,@types1) types1))
(methods (compute-applicable-methods-using-types gf types))
(std-p (accessor-methods-safe-to-use-slot-wrapper-optimizations-p
methods type)))
(declare (type list methods) (type boolean std-p))
(values
(if std-p
(get-optimized-std-accessor-method-function class slotd type)
(let ((wrappers NIL))
(unless (and (eq type 'writer)
(dolist (method methods t)
(unless (eq (car (method-specializers method))
*the-class-t*)
(return nil))))
(setf wrappers (list (wrapper-of class)
(class-wrapper class)
(wrapper-of slotd)))
(when (eq type 'writer)
(setf wrappers (cons (class-wrapper *the-class-t*) wrappers))))
(get-accessor-from-svuc-method-function
class slotd
(get-secondary-dispatch-function
gf methods types
(let ((alist ()))
(dolist (method (reverse methods))
(push (list method
(method-function-for-caching method wrappers))
alist))
alist)
wrappers)
type)))
std-p)))
(defvar *new-class* nil)
;used by optimize-slot-value-by-class-p (vector.lisp)
(defun update-slot-value-gf-info (gf type)
(let* ((old-safe-p
(ecase type
(reader *safe-to-use-slot-value-wrapper-optimizations-p*)
(writer *safe-to-use-set-slot-value-wrapper-optimizations-p*)
(boundp *safe-to-use-slot-boundp-wrapper-optimizations-p*)))
(newly-unsafe-p
(and old-safe-p
(not (accessor-methods-safe-to-use-slot-wrapper-optimizations-p
(generic-function-methods gf) type)))))
(declare (type boolean old-safe-p newly-unsafe-p))
(unless *new-class*
(update-std-or-str-methods gf type))
(when (and (standard-svuc-method type) (structure-svuc-method type))
(flet ((update-class (class)
(when (class-finalized-p class)
(dolist (slotd (class-slots class))
(multiple-value-bind (function std-p)
(get-accessor-method-function gf type class slotd)
#+kcl (si:turbo-closure function)
(setf (slot-accessor-std-p slotd type) std-p)
(update-slot-accessor-function slotd type function T))
(when newly-unsafe-p
(ecase type
(reader (initialize-internal-slot-reader-gfs
(slot-definition-name slotd)))
(writer (initialize-internal-slot-writer-gfs
(slot-definition-name slotd)))
(boundp (initialize-internal-slot-boundp-gfs
(slot-definition-name slotd)))))))))
(if *new-class*
(update-class *new-class*)
(map-all-classes #'update-class 'slot-object))))
(when newly-unsafe-p
(when *always-safe-to-use-slot-wrapper-optimizations-p*
(cerror "Continue even though previously compiled slot-value accesses
might ignore it?"
"Defining user ~S method when PCL was told to assume there
wouldn't be any (~S was set to T)."
(generic-function-name gf)
'*always-safe-to-use-slot-wrapper-optimizations-p*)
(setf *always-safe-to-use-slot-wrapper-optimizations-p* NIL))
(setf *safe-to-use-slot-wrapper-optimizations-p* NIL)
(ecase type
(reader
(setf *safe-to-use-slot-value-wrapper-optimizations-p* NIL))
(writer
(setf *safe-to-use-set-slot-value-wrapper-optimizations-p* NIL))
(boundp
(setf *safe-to-use-slot-boundp-wrapper-optimizations-p* NIL))))
(unless *safe-to-use-slot-wrapper-optimizations-p*
(dolist (gf *generic-functions-having-cached-closures*)
(update-dfun gf)))
(fix-dfuns-needing-update)))
(defvar *standard-slot-value-using-class-method* nil)
(defvar *standard-setf-slot-value-using-class-method* nil)
(defvar *standard-slot-boundp-using-class-method* nil)
(defvar *structure-slot-value-using-class-method* nil)
(defvar *structure-setf-slot-value-using-class-method* nil)
(defvar *structure-slot-boundp-using-class-method* nil)
(defun standard-svuc-method (type)
(case type
(reader *standard-slot-value-using-class-method*)
(writer *standard-setf-slot-value-using-class-method*)
(boundp *standard-slot-boundp-using-class-method*)))
(defun set-standard-svuc-method (type method)
(case type
(reader (setq *standard-slot-value-using-class-method* method))
(writer (setq *standard-setf-slot-value-using-class-method* method))
(boundp (setq *standard-slot-boundp-using-class-method* method))))
(defun structure-svuc-method (type)
(case type
(reader *structure-slot-value-using-class-method*)
(writer *structure-setf-slot-value-using-class-method*)
(boundp *structure-slot-boundp-using-class-method*)))
(defun set-structure-svuc-method (type method)
(case type
(reader (setq *structure-slot-value-using-class-method* method))
(writer (setq *structure-setf-slot-value-using-class-method* method))
(boundp (setq *structure-slot-boundp-using-class-method* method))))
(defun update-std-or-str-methods (gf type)
(dolist (method (generic-function-methods gf))
(let ((specls (method-specializers method)))
(when (and (or (not (eq type 'writer))
(eq (pop specls) *the-class-t*))
(every #'classp specls))
(cond ((and (eq (class-name (car specls))
'std-class)
(eq (class-name (cadr specls))
'standard-object)
(eq (class-name (caddr specls))
'standard-effective-slot-definition))
(set-standard-svuc-method type method))
((and (eq (class-name (car specls))
'structure-class)
(eq (class-name (cadr specls))
'structure-object)
(eq (class-name (caddr specls))
'structure-effective-slot-definition))
(set-structure-svuc-method type method)))))))
(defvar *free-hash-tables* (mapcar #'list '(eq equal eql)))
(defmacro with-hash-table ((table test) &body forms)
`(let* ((.free. (assoc ',test *free-hash-tables*))
(,table (if (cdr .free.)
(pop (cdr .free.))
(make-hash-table :test ',test))))
(multiple-value-prog1
(progn ,@forms)
(clrhash ,table)
(push ,table (cdr .free.)))))
(defmacro with-eq-hash-table ((table) &body forms)
`(with-hash-table (,table eq) ,@forms))
(defmacro with-equal-hash-table ((table) &body forms)
`(with-hash-table (,table equal) ,@forms))
(declaim (ftype (function (T T &optional T)
(values T T T T (or index null) boolean))
make-accessor-table))
(defun make-accessor-table (generic-function type &optional table)
(unless table (setq table (make-hash-table :test 'eq)))
(let ((methods (generic-function-methods generic-function))
(all-index nil)
(no-class-slots-p t)
first second (size 0))
(declare (type index size) (type boolean no-class-slots-p))
;; class -> {(specl slotd)}
(dolist (method methods)
(let* ((specializers (method-specializers method))
(specl (if (eq type 'reader)
(car specializers)
(cadr specializers)))
(standard-object-p (class-standard-p specl))
(slot-name (accessor-method-slot-name method)))
(declare (type boolean standard-object-p))
(when (and (not standard-object-p)
(class-on-class-precedence-list-p
*the-class-structure-object* specl))
(return-from make-accessor-table nil))
(let ((slotd-table (gethash slot-name *name->class->slotd-table*)))
(when slotd-table
(maphash #'(lambda (class slotd)
(when (class-on-class-precedence-list-p specl class)
(unless (and (or standard-object-p
(class-standard-p class))
(slot-accessor-std-p slotd type))
(return-from make-accessor-table nil))
(push (cons specl slotd) (gethash class table))))
slotd-table)))))
(maphash #'(lambda (class specl+slotd-list)
(assure-finalized class)
(dolist (sclass (class-precedence-list class)
(error "This can't happen"))
(let ((a (assq sclass specl+slotd-list)))
(when a
(let* ((slotd (cdr a))
(index (slot-definition-location slotd)))
(unless index (return-from make-accessor-table nil))
(setf (gethash class table) index)
(when (consp index) (setq no-class-slots-p nil))
(setq all-index (if (or (null all-index)
(eql all-index index))
index t))
(setf size (the index (1+ size)))
(cond ((= size 1) (setq first class))
((= size 2) (setq second class)))
(return nil))))))
table)
(values table all-index first second size no-class-slots-p)))
(defun mec-all-classes-internal (spec precompute-p)
(cons (specializer-class spec)
(and (classp spec)
precompute-p
(not (or (eq spec *the-class-t*)
(eq spec *the-class-slot-object*)
(eq spec *the-class-standard-object*)
(eq spec *the-class-structure-object*)))
(let ((sc (class-direct-subclasses spec)))
(when sc
(mapcan #'(lambda (class)
(mec-all-classes-internal class precompute-p))
sc))))))
(defun mec-all-classes (spec precompute-p)
(let ((classes (mec-all-classes-internal spec precompute-p)))
(if (null (cdr classes))
classes
(let* ((a-classes (cons nil classes))
(tail classes))
(loop (when (null (cdr tail))
(return (cdr a-classes)))
(let ((class (cadr tail))
(ttail (cddr tail)))
(if (dolist (c ttail nil)
(when (eq class c) (return t)))
(setf (cdr tail) (cddr tail))
(setf tail (cdr tail)))))))))
(defun mec-all-class-lists (spec-list precompute-p)
(if (null spec-list)
(list nil)
(let* ((car-all-classes (mec-all-classes (car spec-list) precompute-p))
(all-class-lists (mec-all-class-lists (cdr spec-list) precompute-p)))
(mapcan #'(lambda (list)
(mapcar #'(lambda (c) (cons c list)) car-all-classes))
all-class-lists))))
(defun make-emf-cache (generic-function valuep cache classes-list new-class)
(let* ((arg-info (gf-arg-info generic-function))
(nkeys (arg-info-nkeys arg-info))
(metatypes (arg-info-metatypes arg-info))
(wrappers (unless (eq nkeys 1) (make-list nkeys)))
(precompute-p (gf-precompute-dfun-and-emf-p arg-info))
(default '(default)))
(declare (type index nkeys)
(type boolean precompute-p))
(flet ((add-class-list (classes)
(when (or (null new-class) (memq new-class classes))
(let ((wrappers (get-wrappers-from-classes
nkeys wrappers classes metatypes)))
(when (and wrappers
(eq default (probe-cache cache wrappers default)))
(let ((value (cond ((eq valuep t)
(sdfun-for-caching generic-function classes))
((eq valuep :constant-value)
(value-for-caching generic-function classes)))))
(setq cache (fill-cache cache wrappers value t))))))))
(if classes-list
(mapc #'add-class-list classes-list)
(dolist (method (generic-function-methods generic-function))
(mapc #'add-class-list
(mec-all-class-lists (method-specializers method) precompute-p))))
cache)))
(declaim (ftype (function (T) boolean) methods-contain-eql-specializer-p))
(defun methods-contain-eql-specializer-p (methods)
(dolist (method methods nil)
(when (dolist (spec (method-specializers method) nil)
(when (eql-specializer-p spec) (return t)))
(return t))))
(defmacro class-test (arg class)
(cond ((eq class *the-class-t*)
't)
((eq class *the-class-slot-object*)
`(not (eq *the-class-built-in-class*
(wrapper-class (std-instance-wrapper (class-of ,arg))))))
((eq class *the-class-standard-object*)
`(or (std-instance-p ,arg)
(fsc-instance-p ,arg)
(typep ,arg 'standard-object)))
((or (and (structure-class-p class)
(not (eq class *the-class-structure-object*)))
(eq (class-of class) *the-class-built-in-class*))
`(typep ,arg ',(class-name class)))
(t
`(memq ',class (wrapper-class-precedence-list
(fast-wrapper-of ,arg))))))
(defmacro class-eq-test (arg class)
`(eq (class-of ,arg) ',class))
(defmacro eql-test (arg object)
`(eql ,arg ',object))
(defun dnet-methods-p (form)
(and (consp form)
(or (eq (car form) 'methods)
(eq (car form) 'unordered-methods))))
(defmacro scase (arg &rest clauses) ; This is case, but without gensyms
`(let ((.case-arg. ,arg))
(cond ,@(mapcar #'(lambda (clause)
(list* (cond ((listp (car clause))
`(memq .case-arg. ',(car clause)))
((memq (car clause) '(t otherwise))
`t)
(t
`(eql .case-arg. ',(car clause))))
nil
(cdr clause)))
clauses))))
(defmacro mcase (arg &rest clauses) `(scase ,arg ,@clauses))
(defun generate-discrimination-net (generic-function methods types sorted-p)
(let* ((arg-info (gf-arg-info generic-function))
(precedence (arg-info-precedence arg-info)))
(generate-discrimination-net-internal
generic-function methods types
#'(lambda (methods known-types)
(if (or sorted-p
(block one-order-p
(let ((sorted-methods nil))
(map-all-orders
(copy-list methods) precedence
#'(lambda (methods)
(when sorted-methods (return-from one-order-p nil))
(setq sorted-methods methods)))
(setq methods sorted-methods))
t))
`(methods ,methods ,known-types)
`(unordered-methods ,methods ,known-types)))
#'(lambda (position type true-value false-value)
(let ((arg (dfun-arg-symbol position)))
(if (eq (car type) 'eql)
(let* ((false-case-p (and (consp false-value)
(or (eq (car false-value) 'scase)
(eq (car false-value) 'mcase))
(eq arg (cadr false-value))))
(false-clauses (if false-case-p
(cddr false-value)
`((t ,false-value))))
(case-sym (if (and (dnet-methods-p true-value)
(if false-case-p
(eq (car false-value) 'mcase)
(dnet-methods-p false-value)))
'mcase
'scase))
(type-sym (if (memq (cadr type) '(t nil otherwise))
`(,(cadr type))
(cadr type))))
`(,case-sym ,arg
(,type-sym ,true-value)
,@false-clauses))
`(if ,(let ((arg (dfun-arg-symbol position)))
(case (car type)
(class `(class-test ,arg ,(cadr type)))
(class-eq `(class-eq-test ,arg ,(cadr type)))))
,true-value
,false-value))))
#'identity)))
(defun class-from-type (type)
(if (or (atom type) (eq (car type) 't))
*the-class-t*
(case (car type)
(and (dolist (type (cdr type) *the-class-t*)
(when (and (consp type) (not (eq (car type) 'not)))
(return (class-from-type type)))))
(not *the-class-t*)
(eql (class-of (cadr type)))
(class-eq (cadr type))
(class (cadr type)))))
(defun precompute-effective-methods (gf &optional classes-list-p)
(let* ((arg-info (gf-arg-info gf))
(methods (generic-function-methods gf))
(precedence (arg-info-precedence arg-info))
(*in-precompute-effective-methods-p* t)
(classes-list nil))
(generate-discrimination-net-internal
gf methods nil
#'(lambda (methods known-types)
(when methods
(when classes-list-p
(push (mapcar #'class-from-type known-types) classes-list))
(map-all-orders
methods precedence
#'(lambda (methods)
(get-secondary-dispatch-function gf methods known-types)))))
#'(lambda (position type true-value false-value)
(declare (ignore position type true-value false-value))
nil)
#'(lambda (type)
(if (and (consp type) (eq (car type) 'eql))
`(class-eq ,(class-of (cadr type)))
type)))
classes-list))
; we know that known-type implies neither new-type nor `(not ,new-type)
(defun augment-type (new-type known-type)
(if (or (eq known-type 't)
(eq (car new-type) 'eql))
new-type
(let ((so-far (if (and (consp known-type) (eq (car known-type) 'and))
(cdr known-type)
(list known-type))))
(unless (eq (car new-type) 'not)
(setq so-far
(mapcan #'(lambda (type)
(unless (*subtypep new-type type)
(list type)))
so-far)))
(if (null so-far)
new-type
`(and ,new-type ,@so-far)))))
(defun generate-discrimination-net-internal-do-column (p-tail contenders known-types)
(declare (special types methods-function nreq metatypes))
(if p-tail
(let* ((position (car p-tail))
(known-type (or (nth position types) t)))
(declare (type index position))
(if (eq (nth position metatypes) 't)
(generate-discrimination-net-internal-do-column
(cdr p-tail) contenders (cons (cons position known-type) known-types))
(generate-discrimination-net-internal-do-methods
p-tail contenders known-type () known-types)))
(funcall-function methods-function contenders
(let ((k-t (make-list (the index nreq))))
(dolist (index+type known-types)
(setf (nth (the index (car index+type)) k-t)
(cdr index+type)))
k-t))))
(defun generate-discrimination-net-internal-do-methods
(p-tail contenders known-type winners known-types)
(declare (special test-function type-function))
;;
;; <contenders>
;; is a (sorted) list of methods that must be discriminated
;; <known-type>
;; is the type of this argument, constructed from tests already made.
;; <winners>
;; is a (sorted) list of methods that are potentially applicable
;; after the discrimination has been made.
;;
(if (null contenders)
(generate-discrimination-net-internal-do-column
(cdr p-tail) winners (cons (cons (car p-tail) known-type) known-types))
(let* ((position (car p-tail))
(method (car contenders))
(specl (nth position (method-specializers method)))
(type (funcall-function type-function (type-from-specializer specl))))
(declare (type index position))
(multiple-value-bind (app-p maybe-app-p)
(specializer-applicable-using-type-p type known-type)
(declare (type boolean app-p maybe-app-p))
(flet ((determined-to-be (truth-value)
(if truth-value app-p (not maybe-app-p)))
(do-if (truth &optional implied)
(let ((ntype (if truth type `(not ,type))))
(generate-discrimination-net-internal-do-methods p-tail
(cdr contenders)
(if implied
known-type
(augment-type ntype known-type))
(if truth
(append winners `(,method))
winners)
known-types))))
(cond ((determined-to-be nil) (do-if nil t))
((determined-to-be t) (do-if t t))
(t (funcall-function test-function position type
(do-if t) (do-if nil)))))))))
(defun generate-discrimination-net-internal
(gf methods types methods-function test-function type-function)
(declare (special types methods-function test-function type-function))
(let* ((arg-info (gf-arg-info gf))
(precedence (arg-info-precedence arg-info))
(nreq (arg-info-number-required arg-info))
(metatypes (arg-info-metatypes arg-info)))
(declare (type index nreq))
(declare (special nreq metatypes))
(generate-discrimination-net-internal-do-column precedence methods ())))
(defun compute-secondary-dispatch-function (generic-function net &optional
method-alist wrappers)
(funcall-function (compute-secondary-dispatch-function1 generic-function net)
method-alist wrappers))
(defvar *case-table-limit* 10)
(declaim (type index *case-table-limit*))
(defun net-test-converter (form)
(cond ((and (consp form) (eq (car form) 'methods))
'.methods.)
((and (consp form) (eq (car form) 'unordered-methods))
'.umethods.)
((and (consp form) (eq (car form) 'mcase)
(< *case-table-limit* (length (the list (cddr form)))))
'.mcase.)
(t (default-test-converter form))))
(declaim (ftype (function (T T T) (values T list)) net-code-converter))
(defun net-code-converter (form metatypes applyp)
(cond ((and (consp form) (or (eq (car form) 'methods)
(eq (car form) 'unordered-methods)))
(let ((gensym (gensym)))
(values (make-dfun-call metatypes applyp gensym)
(list gensym))))
((and (consp form) (eq (car form) 'mcase)
(< *case-table-limit* (length (the list (cddr form)))))
(let ((gensym (gensym)) (default (gensym)))
(values (make-dfun-call metatypes applyp
`(gethash ,(cadr form) ,gensym ,default))
(list gensym default))))
(t (default-code-converter form))))
(defun net-constant-converter (form generic-function)
(or (let ((c (methods-converter form generic-function)))
(when c (list c)))
(cond ((and (consp form) (eq (car form) 'mcase)
(< *case-table-limit* (length (the list (cddr form)))))
(let* ((list (mapcar #'(lambda (clause)
(let ((key (car clause))
(meth (cadr clause)))
(cons (if (consp key) (car key) key)
(methods-converter meth
generic-function))))
(cddr form)))
(default (car (last list))))
(list (list* '.table. (nbutlast list))
(cdr default))))
(t (default-constant-converter form)))))
(defun methods-converter (form generic-function)
(cond ((and (consp form) (eq (car form) 'methods))
(cons '.methods.
(get-effective-method-function1 generic-function (cadr form))))
((and (consp form) (eq (car form) 'unordered-methods))
(default-secondary-dispatch-function generic-function))))
(defun convert-methods (constant method-alist wrappers)
(if (and (consp constant)
(eq (car constant) '.methods.))
(funcall (cdr constant) method-alist wrappers)
constant))
(defun convert-table (constant method-alist wrappers)
(when (and (consp constant)
(eq (car constant) '.table.))
(let ((table (make-hash-table :test 'eql)))
(dolist (k+v (cdr constant))
(setf (gethash (car k+v) table)
(convert-methods (cdr k+v) method-alist wrappers)))
table)))
(defun compute-secondary-dispatch-function1 (generic-function net)
(if (eq (car net) 'methods)
(get-effective-method-function1 generic-function (cadr net))
(multiple-value-bind (cfunction constants)
(let* ((arg-info (gf-arg-info generic-function))
(metatypes (arg-info-metatypes arg-info))
(applyp (arg-info-applyp arg-info)))
(declare (type boolean applyp))
(get-function1 `(lambda ,(make-dfun-lambda-list metatypes applyp) ,net)
#'net-test-converter
#'(lambda (form)
(net-code-converter form metatypes applyp))
#'(lambda (form)
(net-constant-converter form generic-function))))
#'(lambda (method-alist wrappers)
(apply-function
cfunction
(mapcar #'(lambda (constant)
(or (convert-table constant method-alist wrappers)
(convert-methods constant method-alist wrappers)))
constants))))))
(defvar *show-make-unordered-methods-emf-calls* nil)
(defun make-unordered-methods-emf (generic-function methods)
(when *show-make-unordered-methods-emf-calls*
(format t "~&make-unordered-methods-emf ~s~%"
(generic-function-name generic-function)))
#'(lambda (&rest args)
(let* ((types (types-from-arguments generic-function args 'eql))
(smethods (sort-applicable-methods generic-function methods types))
(emf (get-effective-method-function generic-function smethods)))
(apply-function emf args))))
;;;
;;; NOTE: We are assuming a restriction on user code that the method
;;; combination must not change once it is connected to the
;;; generic function.
;;;
;;; This has to be legal, because otherwise any kind of method
;;; lookup caching couldn't work. See this by saying that this
;;; cache, is just a backing cache for the fast cache. If that
;;; cache is legal, this one must be too.
;;;
;;; Don't clear this table!
(defvar *effective-method-table* (make-hash-table :test 'eq))
(defun get-secondary-dispatch-function (gf methods types &optional
method-alist wrappers)
(funcall-function (get-secondary-dispatch-function1
gf methods types
(not (methods-contain-eql-specializer-p methods)))
method-alist wrappers))
(defun get-secondary-dispatch-function1 (gf methods types
&optional all-applicable-p
(all-sorted-p t))
(if (null methods)
#'(lambda (method-alist wrappers)
(declare (ignore method-alist wrappers))
#'(lambda (&rest args)
(apply #'no-applicable-method gf args)))
(let* ((key (car methods))
(ht-value (or (gethash key *effective-method-table*)
(setf (gethash key *effective-method-table*)
(cons nil nil)))))
(if (and (null (cdr methods)) all-applicable-p) ; the most common case
(or (car ht-value)
(setf (car ht-value)
(get-secondary-dispatch-function2
gf methods types all-applicable-p all-sorted-p)))
(let ((akey (list methods (if all-applicable-p 'all-applicable types))))
(or (cdr (assoc akey (cdr ht-value) :test #'equal))
(let ((value (get-secondary-dispatch-function2
gf methods types all-applicable-p all-sorted-p)))
(push (cons akey value) (cdr ht-value))
value)))))))
(defun get-secondary-dispatch-function2 (gf methods types all-applicable-p all-sorted-p)
(if (and all-applicable-p all-sorted-p)
(let* ((combin (generic-function-method-combination gf))
(effective (compute-effective-method gf combin methods)))
(make-effective-method-function1 gf effective))
(let ((net (generate-discrimination-net
gf methods types all-sorted-p)))
(compute-secondary-dispatch-function1 gf net))))
(defun get-effective-method-function (gf methods &optional method-alist wrappers)
(funcall-function (get-secondary-dispatch-function1 gf methods nil t)
method-alist wrappers))
(defun get-effective-method-function1 (gf methods &optional (sorted-p t))
(get-secondary-dispatch-function1 gf methods nil t sorted-p))
(defun get-dispatch-function (gf)
(let ((methods (generic-function-methods gf)))
(funcall-function (get-secondary-dispatch-function1 gf methods nil nil nil)
nil nil)))
;;;
;;; The value returned by compute-discriminating-function is a function
;;; object. It is called a discriminating function because it is called
;;; when the generic function is called and its role is to discriminate
;;; on the arguments to the generic function and then call appropriate
;;; method functions.
;;;
;;; A discriminating function can only be called when it is installed as
;;; the funcallable instance function of the generic function for which
;;; it was computed.
;;;
;;; More precisely, if compute-discriminating-function is called with an
;;; argument <gf1>, and returns a result <df1>, that result must not be
;;; passed to apply or funcall directly. Rather, <df1> must be stored as
;;; the funcallable instance function of the same generic function <gf1>
;;; (using set-funcallable-instance-function). Then the generic function
;;; can be passed to funcall or apply.
;;;
;;; An important exception is that methods on this generic function are
;;; permitted to return a function which itself ends up calling the value
;;; returned by a more specific method. This kind of `encapsulation' of
;;; discriminating function is critical to many uses of the MOP.
;;;
;;; As an example, the following canonical case is legal:
;;;
;;; (defmethod compute-discriminating-function ((gf my-generic-function))
;;; (let ((std (call-next-method)))
;;; #'(lambda (arg)
;;; (print (list 'call-to-gf gf arg))
;;; (funcall std arg))))
;;;
;;; Because many discriminating functions would like to use a dynamic
;;; strategy in which the precise discriminating function changes with
;;; time it is important to specify how a discriminating function is
;;; permitted itself to change the funcallable instance function of the
;;; generic function.
;;;
;;; Discriminating functions may set the funcallable instance function
;;; of the generic function, but the new value must be generated by making
;;; a call to COMPUTE-DISCRIMINATING-FUNCTION. This is to ensure that any
;;; more specific methods which may have encapsulated the discriminating
;;; function will get a chance to encapsulate the new, inner discriminating
;;; function.
;;;
;;; This implies that if a discriminating function wants to modify itself
;;; it should first store some information in the generic function proper,
;;; and then call compute-discriminating-function. The appropriate method
;;; on compute-discriminating-function will see the information stored in
;;; the generic function and generate a discriminating function accordingly.
;;;
;;; The following is an example of a discriminating function which modifies
;;; itself in accordance with this protocol:
;;;
;;; (defmethod compute-discriminating-function ((gf my-generic-function))
;;; #'(lambda (arg)
;;; (cond (<some condition>
;;; <store some info in the generic function>
;;; (set-funcallable-instance-function
;;; gf
;;; (compute-discriminating-function gf))
;;; (funcall gf arg))
;;; (t
;;; <call-a-method-of-gf>))))
;;;
;;; Whereas this code would not be legal:
;;;
;;; (defmethod compute-discriminating-function ((gf my-generic-function))
;;; #'(lambda (arg)
;;; (cond (<some condition>
;;; (set-funcallable-instance-function
;;; gf
;;; #'(lambda (a) ..))
;;; (funcall gf arg))
;;; (t
;;; <call-a-method-of-gf>))))
;;;
;;; NOTE: All the examples above assume that all instances of the class
;;; my-generic-function accept only one argument.
;;;
;;;
;;;
;;;
(defun gf-dfun-cache (gf)
(let ((state (gf-dfun-state gf)))
(typecase state
(function nil)
(cons (cadr state)))))
(defun gf-dfun-info (gf)
(let ((state (gf-dfun-state gf)))
(typecase state
(function nil)
(cons (cddr state)))))
(defun slot-value-using-class-dfun (class object slotd)
(declare (ignore class))
(method-function-funcall (slot-definition-reader-function slotd) object))
(defun setf-slot-value-using-class-dfun (new-value class object slotd)
(declare (ignore class))
(method-function-funcall (slot-definition-writer-function slotd) new-value object))
(defun slot-boundp-using-class-dfun (class object slotd)
(declare (ignore class))
(method-function-funcall (slot-definition-boundp-function slotd) object))
(defmethod compute-discriminating-function ((gf standard-generic-function))
(with-slots (dfun-state arg-info) gf
(typecase dfun-state
(null (let ((name (generic-function-name gf)))
(when (eq name 'compute-applicable-methods)
(update-all-c-a-m-gf-info gf))
(cond ((eq name 'slot-value-using-class)
(update-slot-value-gf-info gf 'reader)
#'slot-value-using-class-dfun)
((equal name '(setf slot-value-using-class))
(update-slot-value-gf-info gf 'writer)
#'setf-slot-value-using-class-dfun)
((eq name 'slot-boundp-using-class)
(update-slot-value-gf-info gf 'boundp)
#'slot-boundp-using-class-dfun)
((gf-precompute-dfun-and-emf-p arg-info)
(make-final-dfun gf))
(t
(make-initial-dfun gf)))))
(function dfun-state)
(cons (car dfun-state)))))
(defun set-dfun (generic-function &optional dfun cache info)
(setf (gf-dfun-state generic-function)
(if (and dfun (or cache info))
(list* dfun cache info)
dfun))
dfun)
(defun update-dfun (generic-function &optional dfun cache info)
(let ((ocache (gf-dfun-cache generic-function)))
(set-dfun generic-function dfun cache info)
(let ((dfun (compute-discriminating-function generic-function))
(gf-name (generic-function-name generic-function)))
(unless (eq 'default-method-only (type-of (gf-dfun-info generic-function)))
(setq dfun (doctor-dfun-for-the-debugger
generic-function
#+cmu dfun #-cmu (set-function-name dfun gf-name))))
(set-funcallable-instance-function generic-function dfun)
#+cmu (set-function-name generic-function gf-name)
(when (and ocache (not (eq ocache cache))) (free-cache ocache))
(setf *dfuns-needing-update* (remove gf-name *dfuns-needing-update*))
dfun)))
(defmethod update-gf-dfun ((class std-class) gf)
(let ((*new-class* class)
(name (generic-function-name gf))
(arg-info (gf-arg-info gf)))
(cond ((eq name 'slot-value-using-class)
(update-slot-value-gf-info gf 'reader))
((equal name '(setf slot-value-using-class))
(update-slot-value-gf-info gf 'writer))
((eq name 'slot-boundp-using-class)
(update-slot-value-gf-info gf 'boundp))
((gf-precompute-dfun-and-emf-p arg-info)
(multiple-value-bind (dfun cache info)
(make-final-dfun-internal gf)
(set-dfun gf dfun cache info) ; otherwise cache might get freed twice
(update-dfun gf dfun cache info))))))
;;;
;;;
;;;
(declaim (ftype (function (T) (values list boolean)) function-keywords))
(defmethod function-keywords ((method standard-method))
(multiple-value-bind (nreq nopt keysp restp allow-other-keys-p keywords)
(analyze-lambda-list (method-lambda-list method))
(declare (type boolean allow-other-keys-p))
(declare (ignore nreq nopt keysp restp))
(values keywords allow-other-keys-p)))
(defun method-ll->generic-function-ll (ll)
;; The generic-function lambda-list is the same as the method-lambda
;; list, except that no default initial values or supplied-p
;; parameters are allowed for optional or keyword arguments, and
;; &aux parameters are not allowed (and therefore removed).
(let ((collection NIL))
(dolist (element ll)
(cond ((listp element)
(push (car element) collection))
((eq element '&aux)
(return))
(T (push element collection))))
(nreverse collection)))
;;;
;;; This is based on the rules of method lambda list congruency defined in
;;; the spec. The lambda list it constructs is the pretty union of the
;;; lambda lists of all the methods. It doesn't take method applicability
;;; into account at all yet.
;;;
(declaim (ftype (function (T) (values list list boolean list boolean))
method-pretty-arglist))
(defmethod method-pretty-arglist ((method standard-method))
(let ((required ())
(optional ())
(rest-p nil)
(key ())
(allow-other-keys-p nil)
(state 'required)
(arglist (method-lambda-list method)))
(declare (type boolean rest-p allow-other-keys-p))
(dolist (arg arglist)
(cond ((eq arg '&optional) (setq state 'optional))
((eq arg '&rest) (setq state 'rest))
((eq arg '&key) (setq state 'key))
((eq arg '&allow-other-keys) (setq allow-other-keys-p 't))
((memq arg lambda-list-keywords))
(t
(ecase state
(required (push arg required))
(optional (push arg optional))
(key (push arg key))
(rest (setq rest-p arg))))))
(values (nreverse required)
(nreverse optional)
rest-p
(nreverse key)
allow-other-keys-p)))
(defmethod generic-function-pretty-arglist
((generic-function standard-generic-function))
(let ((methods (generic-function-methods generic-function))
(arglist ()))
(when methods
(multiple-value-bind (required optional rest key allow-other-keys)
(method-pretty-arglist (car methods))
(dolist (m (cdr methods))
(multiple-value-bind (method-key-keywords
method-allow-other-keys
method-key)
(function-keywords m)
;; we've modified function-keywords to return what we want as
;; the third value, no other change here.
(declare (ignore method-key-keywords))
(setq key (union key method-key))
(setq allow-other-keys (or allow-other-keys
method-allow-other-keys))))
(when allow-other-keys
(setq arglist '(&allow-other-keys)))
(when key
(setq arglist (nconc (list '&key) key arglist)))
(when rest
(setq arglist (nconc (list '&rest rest) arglist)))
(when optional
(setq arglist (nconc (list '&optional) optional arglist)))
(nconc required arglist)))))